/**************************************************************************************************
  Filename:       Cent.c
  Revised:        $Date: 2011-06-20 11:57:59 -0700 (Mon, 20 Jun 2011) $
  Revision:       $Revision: 28 $

  Description:    This file contains the Simple BLE Central sample application 
                  for use with the CC2540 Bluetooth Low Energy Protocol Stack.

**************************************************************************************************/

/*********************************************************************
 * INCLUDES
 */

// #include "linkdb.h"

#include "Cent.h"
/*********************************************************************
 * MACROS
 */

// Length of bd addr as a string
#define B_ADDR_STR_LEN                        15

/*********************************************************************
 * CONSTANTS
 */

// Maximum number of scan responses
#define DEFAULT_MAX_SCAN_RES                  8

// Scan duration in ms
#define DEFAULT_SCAN_DURATION                 4000

// Discovey mode (limited, general, all)
#define DEFAULT_DISCOVERY_MODE                DEVDISC_MODE_GENERAL
 // DEVDISC_MODE_ALL

// TRUE to use active scan
#define DEFAULT_DISCOVERY_ACTIVE_SCAN         TRUE

// TRUE to use white list during discovery
#define DEFAULT_DISCOVERY_WHITE_LIST          FALSE

// TRUE to use high scan duty cycle when creating link
#define DEFAULT_LINK_HIGH_DUTY_CYCLE          FALSE

// TRUE to use white list when creating link
#define DEFAULT_LINK_WHITE_LIST               FALSE

// Default RSSI polling period in ms
#define DEFAULT_RSSI_PERIOD                   1000

// Whether to enable automatic parameter update request when a connection is formed
#define DEFAULT_ENABLE_UPDATE_REQUEST         TRUE

// Minimum connection interval (units of 1.25ms) if automatic parameter update request is enabled
#define DEFAULT_UPDATE_MIN_CONN_INTERVAL      6

// Maximum connection interval (units of 1.25ms) if automatic parameter update request is enabled
#define DEFAULT_UPDATE_MAX_CONN_INTERVAL      800

// Slave latency to use if automatic parameter update request is enabled
#define DEFAULT_UPDATE_SLAVE_LATENCY          0

// Supervision timeout value (units of 10ms) if automatic parameter update request is enabled
#define DEFAULT_UPDATE_CONN_TIMEOUT           10000

// Default GAP pairing mode
#define DEFAULT_PAIRING_MODE                  GAPBOND_PAIRING_MODE_INITIATE

// Default MITM mode (TRUE to require passcode or OOB when pairing)
#define DEFAULT_MITM_MODE                     FALSE

// Default bonding mode, TRUE to bond
#define DEFAULT_BONDING_MODE                  TRUE

// Default GAP bonding I/O capabilities
#define DEFAULT_IO_CAPABILITIES               GAPBOND_IO_CAP_DISPLAY_ONLY

// Default service discovery timer delay in ms
#define DEFAULT_SVC_DISCOVERY_DELAY           1000

#define DISPLAY_DISC_DELAY          1000
#define PROCESS_DISC_DELAY          1000
#define CONNECTION_UPDATE_DELAY     3000
#define ESTABLIST_LINK_DELAY        3000
#define TERMINATE_CONNECTION_DELAY     100000
#define TOGGLE_RSSI_POLLING_DELAY   1000
#define _debug_ 1

// TRUE to filter discovery results on desired service UUID
#define DEFAULT_DEV_DISC_BY_SVC_UUID          TRUE

// Application states
enum
{
  BLE_STATE_IDLE,
  BLE_STATE_CONNECTING,
  BLE_STATE_CONNECTED,
  BLE_STATE_DISCONNECTING
};

// Discovery states
enum
{
  BLE_DISC_STATE_IDLE,                // Idle
  BLE_DISC_STATE_SVC,                 // Service discovery
  BLE_DISC_STATE_CHAR                 // Characteristic discovery
};

/*********************************************************************
 * TYPEDEFS
 */

/*********************************************************************
 * GLOBAL VARIABLES
 */
volatile uint32 __currentTimeUnix;

uint8 centBLETaskId;
uint16 centConnHandle = GAP_CONNHANDLE_INIT;;
  

/*********************************************************************
 * EXTERNAL VARIABLES
 */

/*********************************************************************
 * EXTERNAL FUNCTIONS
 */

/*********************************************************************
 * LOCAL VARIABLES
 */
// GAP GATT Attributes
static const uint8 centBLEDeviceName[GAP_DEVICE_NAME_LEN] = "Cent";

// Number of scan results and scan result index
static uint8 centBLEScanRes;
static uint8 centBLEScanIdx;

// Scan result list
static gapDevRec_t centBLEDevList[DEFAULT_MAX_SCAN_RES];

// Scanning state
static uint8 centBLEScanning = FALSE;

// RSSI polling state
static uint8 centBLERssi = FALSE;

// Connection handle of current connection 
// static uint16 centConnHandle = GAP_CONNHANDLE_INIT;

// Application state
static uint8 centBLEState = BLE_STATE_IDLE;

// Discovery state
static uint8 centBLEDiscState = BLE_DISC_STATE_IDLE;

// Discovered service start and end handle
static uint16 centBLESvcStartHdl = 0;
static uint16 centBLESvcEndHdl = 0;

// Discovered characteristic handle
static uint16 centBLECharHdl = 0;

// Value to write
static uint8 centBLECharVal = 0;

// Value read/write toggle
static bool centBLEDoWrite = FALSE;

// GATT read/write procedure state
static bool centBLEProcedureInProgress = FALSE;


// Service discovery state
static uint8 centDiscState = DISC_IDLE;

// Service discovery complete
static uint8 centDiscoveryCmpl = FALSE;

// Characteristic configuration state
static uint8 centConfigState = CENT_CONFIG_START;

/*********************************************************************
 * LOCAL FUNCTIONS
 */
static void CentProcessGATTMsg( gattMsgEvent_t *pMsg );
static void CentRssiCB( uint16 connHandle, int8  rssi );
static void CentEventCB( gapCentralRoleEvent_t *pEvent );
static void CentPasscodeCB( uint8 *deviceAddr, uint16 connectionHandle,
                                        uint8 uiInputs, uint8 uiOutputs );
static void CentPairStateCB( uint16 connHandle, uint8 state, uint8 status );
// - static void Cent_HandleKeys( uint8 shift, uint8 keys );
static void Cent_ProcessOSALMsg( osal_event_hdr_t *pMsg );
static void centBLEGATTDiscoveryEvent( gattMsgEvent_t *pMsg );
static void CentStartDiscovery( void );
static bool centBLEFindSvcUuid( uint16 uuid, uint8 *pData, uint8 dataLen );
static void centBLEAddDeviceInfo( uint8 *pAddr, uint8 addrType );
char *bdAddr2Str ( uint8 *pAddr );
// static void dataChangeCB(uint8 paramID); 
static void WatcherHeimdallr_HandleSerial(mtOSALSerialData_t *cmdMsg);


static void processDiscovery(void);
static void displayDiscResult(void);
static void conUpdate(void);
static void terminateLink(void);
// static void toggleConnect(void);
static void toggleRSSIPolling(void);
static void processConnect(void);

/*********************************************************************
 * PROFILE CALLBACKS
 */

// GAP Role Callbacks
static const gapCentralRoleCB_t centBLERoleCB =
{
  CentRssiCB,       // RSSI callback
  CentEventCB       // Event callback
};

// Bond Manager Callbacks
static const gapBondCBs_t centBLEBondCB =
{
  CentPasscodeCB,
  CentPairStateCB
};

// static dataCBs_t centDataCBs =
// {
//   dataChangeCB,          // Called when Enabler attribute changes
// };
/*********************************************************************
 * PUBLIC FUNCTIONS
 */

/*********************************************************************
 * @fn      Cent_Init
 *
 * @brief   Initialization function for the Simple BLE Central App Task.
 *          This is called during initialization and should contain
 *          any application specific initialization (ie. hardware
 *          initialization/setup, table initialization, power up
 *          notification).
 *
 * @param   task_id - the ID assigned by OSAL.  This ID should be
 *                    used to send messages and set timers.
 *
 * @return  none
 */
void Cent_Init( uint8 task_id )
{
  centBLETaskId = task_id;
  BSP_LED_INIT();
  
  UartInit();
  RegisterForSerial(centBLETaskId);
  // Setup Central Profile
  {
    uint8 scanRes = DEFAULT_MAX_SCAN_RES;
    GAPCentralRole_SetParameter ( GAPCENTRALROLE_MAX_SCAN_RES, sizeof( uint8 ), &scanRes );
  }
  BattLED_OFF();
  SignalLED_FLASH();
   // Setup GAP
  GAP_SetParamValue( TGAP_GEN_DISC_SCAN, DEFAULT_SCAN_DURATION );
  // GAP_SetParamValue( TGAP_LIM_DISC_SCAN, DEFAULT_SCAN_DURATION );
  GGS_SetParameter( GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, (uint8 *) centBLEDeviceName );

  // Setup the GAP Bond Manager
  {
    uint32 passkey = MAXTAIN_PASSCODE;
    uint8 pairMode = DEFAULT_PAIRING_MODE;
    uint8 mitm = DEFAULT_MITM_MODE;
    uint8 ioCap = DEFAULT_IO_CAPABILITIES;
    uint8 bonding = DEFAULT_BONDING_MODE;
    GAPBondMgr_SetParameter( GAPBOND_DEFAULT_PASSCODE, sizeof( uint32 ), &passkey );
    GAPBondMgr_SetParameter( GAPBOND_PAIRING_MODE, sizeof( uint8 ), &pairMode );
    GAPBondMgr_SetParameter( GAPBOND_MITM_PROTECTION, sizeof( uint8 ), &mitm );
    GAPBondMgr_SetParameter( GAPBOND_IO_CAPABILITIES, sizeof( uint8 ), &ioCap );
    GAPBondMgr_SetParameter( GAPBOND_BONDING_ENABLED, sizeof( uint8 ), &bonding );
  }  



  // GAP_SetParamValue( TGAP_CONN_EST_INT_MIN, DEFAULT_UPDATE_MIN_CONN_INTERVAL );
  // GAP_SetParamValue( TGAP_CONN_EST_INT_MAX, DEFAULT_UPDATE_MAX_CONN_INTERVAL); 
  // GAP_SetParamValue( TGAP_CONN_EST_LATENCY , DEFAULT_UPDATE_SLAVE_LATENCY );
  // GAP_SetParamValue( TGAP_CONN_EST_SUPERV_TIMEOUT ,DEFAULT_UPDATE_CONN_TIMEOUT );
  dbg("hello\n");
  // Initialize GATT Client
  VOID GATT_InitClient();

  // Register to receive incoming ATT Indications/Notifications
  GATT_RegisterForInd( centBLETaskId );

  // Initialize GATT attributes
  GGS_AddService( GATT_ALL_SERVICES );         // GAP
  GATTServApp_AddService( GATT_ALL_SERVICES ); // GATT attributes
  // Data_AddService( GATT_ALL_SERVICES );

  // Data_RegisterAppCBs( &centDataCBs );
  // Register for all key events - This app will handle all key events
  // - RegisterForKeys( centBLETaskId );
  
  // makes sure LEDs are off
  // - HalLedSet( (HAL_LED_1 | HAL_LED_2), HAL_LED_MODE_OFF );
  
  // Setup a delayed profile startup
  dbg("ttt\n");
  osal_set_event( centBLETaskId, START_DEVICE_EVT );
}

/*********************************************************************
 * @fn      Cent_ProcessEvent
 *
 * @brief   Simple BLE Central Application Task event processor.  This function
 *          is called to process all events for the task.  Events
 *          include timers, messages and any other user defined events.
 *
 * @param   task_id  - The OSAL assigned task ID.
 * @param   events - events to process.  This is a bit map and can
 *                   contain more than one event.
 *
 * @return  events not processed
 */
uint16 Cent_ProcessEvent( uint8 task_id, uint16 events )
{
  
  VOID task_id; // OSAL required parameter that isn't used in this function
 
  if ( events & SYS_EVENT_MSG )
  {
    uint8 *pMsg;

    if ( (pMsg = osal_msg_receive( centBLETaskId )) != NULL )
    {
      Cent_ProcessOSALMsg( (osal_event_hdr_t *)pMsg );

      // Release the OSAL message
      VOID osal_msg_deallocate( pMsg );
    }

    // return unprocessed events
    return (events ^ SYS_EVENT_MSG);
  }

  if ( events & START_DEVICE_EVT )
  {
    // Start the Device
    VOID GAPCentralRole_StartDevice( (gapCentralRoleCB_t *) &centBLERoleCB );
    // Register with bond manager after starting device
    GAPBondMgr_Register( (gapBondCBs_t *) &centBLEBondCB );
    
    dbg("h00\n\n");
    osal_start_timerEx(centBLETaskId, PROC_DISC_EVT , PROCESS_DISC_DELAY);
    return ( events ^ START_DEVICE_EVT );
  }

  if ( events & START_DISCOVERY_EVT )
  {
    // conUpdate();
    CentStartDiscovery();      // 设置好 UUID 
    centDiscState = centDiscStart();
    return ( events ^ START_DISCOVERY_EVT );
  }
  if ( events & PROC_DISC_EVT)
  { 
    processDiscovery();
    dbg("h2\n");
    if (1)
    {
      // osal_start_timerEx(centBLETaskId,START_DISCOVERY_EVT,1000);
    }
    return ( events ^ PROC_DISC_EVT );
  }
  if (events & DISPLAY_DISC_RESULT_EVT)
  {
    dbg("h3\n");
    displayDiscResult();
    return ( events ^ DISPLAY_DISC_RESULT_EVT );
  }
  if (events & ESTABLIST_LINK_EVT)
  {
    dbg("\nh4\n");
    processConnect();
    return (events ^ ESTABLIST_LINK_EVT);
  }
  if ( events & CONNECTION_UPDATE_EVT )
  {
    dbg("h5\n");

    // conUpdate();
    if (1)
    {
      // osal_start_timerEx(centBLETaskId, CONNECTION_UPDATE_EVT , CONNECTION_UPDATE_DELAY);
    }
    return ( events ^ CONNECTION_UPDATE_EVT );
  }
  // osal_start_timerEx(centBLETaskId, TERMINATE_CONNECTION_EVT , TERMINATE_CONNECTION_DELAY);
  if ( events & TERMINATE_CONNECTION_EVT)
  {    
    dbg("h6\n");
    terminateLink();
    return ( events ^ TERMINATE_CONNECTION_EVT );
  }
  if ( events & TOGGLE_RSSI_POLLING_EVT )
  {
    dbg("\nh7\n");
    toggleRSSIPolling();
    if ( centBLERssi )
      osal_start_timerEx(centBLETaskId,TOGGLE_RSSI_POLLING_EVT,1000);
    
    // osal_start_timerEx(centBLETaskId, CONNECTION_UPDATE_EVT ,  10 * CONNECTION_UPDATE_DELAY);
    return ( events ^ TOGGLE_RSSI_POLLING_EVT );
  }
  // Discard unknown events
  return 0;
}

/*********************************************************************
 * @fn      Cent_ProcessOSALMsg
 *
 * @brief   Process an incoming task message.
 *
 * @param   pMsg - message to process
 *
 * @return  none
 */
static void Cent_ProcessOSALMsg( osal_event_hdr_t *pMsg )
{
  // dbg("helo");
  switch ( pMsg->event )
  {
    // - case KEY_CHANGE:
    // -  Cent_HandleKeys( ((keyChange_t *)pMsg)->state, ((keyChange_t *)pMsg)->keys );
    // -  break;
    case SERIAL_MSG:
      WatcherHeimdallr_HandleSerial( (mtOSALSerialData_t *)pMsg );
      break;

    case GATT_MSG_EVENT:
      CentProcessGATTMsg( (gattMsgEvent_t *) pMsg );
      break;
    default:
      break;
  }
}

static void WatcherHeimdallr_HandleSerial(mtOSALSerialData_t *cmdMsg)
{
  uint8 i,len,*str=NULL;   
  str=cmdMsg->msg;        
  len=*str;               
  for(i=1;i<=len;i++)
    HalUARTWrite(0,str+i,1 ); 
  HalUARTWrite(0,"\r\n",2); 
}
/*********************************************************************
 * @fn      Cent_HandleKeys
 *
 * @brief   Handles all key events for this device.
 *
 * @param   shift - true if in shift/alt.
 * @param   keys - bit field for key events. Valid entries:
 *                 HAL_KEY_SW_2
 *                 HAL_KEY_SW_1
 *
 * @return  none
 */

uint8 gStatus;

static void processDiscovery()
{
  // Start or stop discovery
  if ( centBLEState != BLE_STATE_CONNECTED )
  {
    if ( !centBLEScanning )
    {
      centBLEScanning = TRUE;
      centBLEScanRes = 0;
      
      dbg( "Discovering...\n");
      
      GAPCentralRole_StartDiscovery( DEFAULT_DISCOVERY_MODE,
                                     DEFAULT_DISCOVERY_ACTIVE_SCAN,
                                     DEFAULT_DISCOVERY_WHITE_LIST );      
    }
    else
    {
      GAPCentralRole_CancelDiscovery();
    }
  }
  else if ( centBLEState == BLE_STATE_CONNECTED &&
            centBLECharHdl != 0 &&
            centBLEProcedureInProgress == FALSE )
  {
    uint8 status;
    
    // Do a read or write as long as no other read or write is in progress
    if ( centBLEDoWrite )
    {
      // Do a write
      attWriteReq_t req;
      
      req.handle = centBLECharHdl;
      req.len = 1;
      req.value[0] = centBLECharVal;
      req.sig = 0;
      req.cmd = 0;
      status = GATT_WriteCharValue( centConnHandle, &req, centBLETaskId );         
    }
    else
    {
      // Do a read
      attReadReq_t req;
      
      req.handle = centBLECharHdl;
      status = GATT_ReadCharValue( centConnHandle, &req, centBLETaskId );
    }
    
    if ( status == SUCCESS )
    {
      centBLEProcedureInProgress = TRUE;
      centBLEDoWrite = !centBLEDoWrite;
    }
  }
}
static void displayDiscResult()
{
  // Display discovery results
  if ( !centBLEScanning && centBLEScanRes > 0 )
  {
      // Increment index of current result (with wraparound)
      centBLEScanIdx++;
      if ( centBLEScanIdx >= centBLEScanRes )
      {
        centBLEScanIdx = 0;
      }
      
      dbg( "Device: %d\n", centBLEScanIdx + 1);
      dbg( "%s\n",bdAddr2Str( centBLEDevList[centBLEScanIdx].addr ));
  }
}
static void conUpdate(void)
{
  // Connection update
  if ( centBLEState == BLE_STATE_CONNECTED )
  {
    GAPCentralRole_UpdateLink( centConnHandle,
                               DEFAULT_UPDATE_MIN_CONN_INTERVAL,
                               DEFAULT_UPDATE_MAX_CONN_INTERVAL,
                               DEFAULT_UPDATE_SLAVE_LATENCY,
                               DEFAULT_UPDATE_CONN_TIMEOUT );
  }
}
static void processConnect(void)
{
  uint8 addrType;
  uint8 *peerAddr;
  
  // Connect or disconnect
  if ( centBLEState == BLE_STATE_IDLE )
  {
    // if there is a scan result
    if ( centBLEScanRes > 0 )
    {
      osal_start_timerEx(centBLETaskId, DISPLAY_DISC_RESULT_EVT , DISPLAY_DISC_DELAY);
      // connect to current device in scan result
      peerAddr = centBLEDevList[centBLEScanIdx].addr;
      addrType = centBLEDevList[centBLEScanIdx].addrType;
    
      centBLEState = BLE_STATE_CONNECTING;
      
      bStatus_t tmp =  GAPCentralRole_EstablishLink( DEFAULT_LINK_HIGH_DUTY_CYCLE,
                                    DEFAULT_LINK_WHITE_LIST,
                                    addrType, peerAddr );

      
      dbg( "Status:0x%x\nBLE State:0x%x\nConnecting:%s\n" , tmp,centBLEState,bdAddr2Str( peerAddr ));
      osal_start_timerEx(centBLETaskId, CONNECTION_UPDATE_EVT , CONNECTION_UPDATE_DELAY);
    }
  }
}
static void terminateLink(void)
{
  if ( centBLEState == BLE_STATE_CONNECTING ||
            centBLEState == BLE_STATE_CONNECTED )
  {
    // disconnect
    centBLEState = BLE_STATE_DISCONNECTING;

    gStatus = GAPCentralRole_TerminateLink( centConnHandle );
    
    dbg( "Disconnecting"); 
  }
}
/*static void toggleConnect(void)
{
  uint8 addrType;
  uint8 *peerAddr;
  
  // Connect or disconnect
  if ( centBLEState == BLE_STATE_IDLE )
  {
    // if there is a scan result
    if ( centBLEScanRes > 0 )
    {
      // connect to current device in scan result
      peerAddr = centBLEDevList[centBLEScanIdx].addr;
      addrType = centBLEDevList[centBLEScanIdx].addrType;
    
      centBLEState = BLE_STATE_CONNECTING;
      
      GAPCentralRole_EstablishLink( DEFAULT_LINK_HIGH_DUTY_CYCLE,
                                    DEFAULT_LINK_WHITE_LIST,
                                    addrType, peerAddr );

      dbg( "Connecting:" );
      dbg( "%s\n", bdAddr2Str( peerAddr )); 
    }
  }
  else if ( centBLEState == BLE_STATE_CONNECTING ||
            centBLEState == BLE_STATE_CONNECTED )
  {
    // disconnect
    centBLEState = BLE_STATE_DISCONNECTING;

    gStatus = GAPCentralRole_TerminateLink( centConnHandle );
    
    dbg( "Disconnecting" ); 
  }
}*/

static void toggleRSSIPolling(void)
{
  // Start or cancel RSSI polling
  if ( centBLEState == BLE_STATE_CONNECTED )
  {
    if ( !centBLERssi )
    {
      centBLERssi = TRUE;
      GAPCentralRole_StartRssi( centConnHandle, DEFAULT_RSSI_PERIOD );
    }
    else
    {
      centBLERssi = FALSE;
      GAPCentralRole_CancelRssi( centConnHandle );
      
      dbg( "RSSI Cancelled" );
      osal_stop_timerEx(centBLETaskId,TOGGLE_RSSI_POLLING_EVT);
    }
  }
}
 static void CentStartBatt( void );
/*********************************************************************
 * @fn      CentProcessGATTMsg
 *
 * @brief   Process GATT messages
 *
 * @return  none
 */
static void CentProcessGATTMsg( gattMsgEvent_t *pMsg )
{
  // dbg(">>>>>>>>>>>>>>>>>>>>>>xxxx\n");
  if ( centBLEState != BLE_STATE_CONNECTED )
  {
    // In case a GATT message came after a connection has dropped,
    // ignore the message
    dbg(">>>>>>>>>>> c1:%x\n",centBLEState);
    return;
  }
  if ( pMsg->method == ATT_HANDLE_VALUE_NOTI ||
       pMsg->method == ATT_HANDLE_VALUE_IND )
  {
    //need to give a condition of ENABLED NOTIFY
    dbg(">>> centNotiGattMsg\n");
    centNotiGattMsg( pMsg );    //need
//get out 
  }else if ( ( pMsg->method == ATT_READ_RSP || pMsg->method == ATT_WRITE_RSP ) ||
            ( pMsg->method == ATT_ERROR_RSP &&
              ( pMsg->msg.errorRsp.reqOpcode == ATT_READ_REQ ||
                pMsg->msg.errorRsp.reqOpcode == ATT_WRITE_REQ ) ) )
  {
    dbg(">> centConfigGattMsg\n");
    centConfigState = centConfigGattMsg ( centConfigState, pMsg );
    if ( centConfigState == CENT_CONFIG_CMPL )
    {
      centDiscoveryCmpl = TRUE;
    }
    centBLEProcedureInProgress = FALSE;
  }
  else
  {
    dbg(">>>>> CentProcessGATTMsg\n");
    centDiscState = centDiscGattMsg( centDiscState, pMsg );
    // if (centDiscState == DISC_BATT_SVC)
    // {
    //   dbg("CentStartBatt\n");
    //   CentStartBatt();
    // }
    if ( centDiscState == DISC_IDLE )
    {      
      dbg("centConfigNext\n");
      // Start characteristic configuration
      centDiscState = centConfigNext( CENT_CONFIG_START );
    }
  }
  // if (pMsg->method == ATT_ERROR_RSP && centDiscState == DISC_BATT_SVC){
  //   CentStartBatt();
  // }
  // else if ( centBLEDiscState != BLE_DISC_STATE_IDLE )
  // {
  //   dbg("c5\n");
  //   centBLEGATTDiscoveryEvent( pMsg );
  // }
  
}

/*********************************************************************
 * @fn      CentRssiCB
 *
 * @brief   RSSI callback.
 *
 * @param   connHandle - connection handle
 * @param   rssi - RSSI
 *
 * @return  none
 */
static void CentRssiCB( uint16 connHandle, int8 rssi )
{
    dbg( "RSSI -dB:%d", (uint8) (-rssi) );
}

/*********************************************************************
 * @fn      CentEventCB
 *
 * @brief   Central event callback function.
 *
 * @param   pEvent - pointer to event structure
 *
 * @return  none
 */
static void CentEventCB( gapCentralRoleEvent_t *pEvent )
{
  dbg(">test\n");
  switch ( pEvent->gap.opcode )
  {
    case GAP_DEVICE_INIT_DONE_EVENT:  
      {
        dbg( "BLE Central" );
        dbg( "%s\n",bdAddr2Str( pEvent->initDone.devAddr));
      }
      break;

    case GAP_DEVICE_INFO_EVENT:
      {
        // if filtering device discovery results based on service UUID
        if ( DEFAULT_DEV_DISC_BY_SVC_UUID == TRUE )
        {
          dbg("tt2\n");
          if ( centBLEFindSvcUuid(  DATA_SERV_UUID,
                                     pEvent->deviceInfo.pEvtData,
                                     pEvent->deviceInfo.dataLen ) )
          {
            dbg("tttx\n");
            centBLEAddDeviceInfo( pEvent->deviceInfo.addr, pEvent->deviceInfo.addrType );
          }
        }
      }
      break;
      
    case GAP_DEVICE_DISCOVERY_EVENT:
      {
        // discovery complete
        centBLEScanning = FALSE;

        // if not filtering device discovery results based on service UUID
        if ( DEFAULT_DEV_DISC_BY_SVC_UUID == FALSE )
        {
          // Copy results
          dbg("FALSE\n");
          centBLEScanRes = pEvent->discCmpl.numDevs;
          osal_memcpy( centBLEDevList, pEvent->discCmpl.pDevList,
                       (sizeof( gapDevRec_t ) * pEvent->discCmpl.numDevs) );
        }
        
        dbg( "Devices Found:%d\n", centBLEScanRes);
        if ( centBLEScanRes > 0 )
        {
          dbg( "<- To Select" );
        }

        // initialize scan index to last device
        centBLEScanIdx = 0;//centBLEScanRes;
        // process connect immediatially
        osal_start_timerEx(centBLETaskId,ESTABLIST_LINK_EVT,500);
        // processConnect();
      }
      break;

    case GAP_LINK_ESTABLISHED_EVENT:
      {

        if ( pEvent->gap.hdr.status == SUCCESS )
        {
          // linkDBItem_t  *pItem;
          centBLEState = BLE_STATE_CONNECTED;
          centConnHandle = pEvent->linkCmpl.connectionHandle;

          centBLEProcedureInProgress = TRUE;
          // If connected to device without bond do service discovery
          // if (!pEvent->linkCmpl.devAddr)
          // {
            
          // }
          // If service discovery not performed initiate service discovery
          // if ( centBLECharHdl == 0 )
          
          if ( centDiscoveryCmpl == FALSE )
          {
            dbg(">come on:%x\n",centConnHandle);
            osal_start_timerEx( centBLETaskId, START_DISCOVERY_EVT, DEFAULT_SVC_DISCOVERY_DELAY );
          } 
          else
          {
            centConfigState = centConfigNext( CENT_CONFIG_CONN_START );
          }
                    
         dbg( "connected:" );
         dbg( "%s\n", bdAddr2Str( pEvent->linkCmpl.devAddr ) );   
         // Data_EnableNotify(centConnHandle);
        }
        else
        {
          centBLEState = BLE_STATE_IDLE;
          centConnHandle = GAP_CONNHANDLE_INIT;
          centBLERssi = FALSE;
          centBLEDiscState = BLE_DISC_STATE_IDLE;
          
          centDiscoveryCmpl = FALSE;
  
          dbg( "Connect Failed" );
          dbg( "Reason:%d\n", pEvent->gap.hdr.status );
        }
        
      }
      break;

    case GAP_LINK_TERMINATED_EVENT:
      {
        centBLEState = BLE_STATE_IDLE;
        centConnHandle = GAP_CONNHANDLE_INIT;
        centBLERssi = FALSE;
        centBLEDiscState = BLE_DISC_STATE_IDLE;
        centBLECharHdl = 0;
        centBLEProcedureInProgress = FALSE;
          
        dbg( "Disconnected\n" );
        dbg( "Reason:%d\n", pEvent->linkTerminate.reason);
        // osal_set_event( centBLETaskId, START_DEVICE_EVT );
      }
      break;

    case GAP_LINK_PARAM_UPDATE_EVENT:
      {
        dbg( "Param Update\n" );
      }
      break;
      
    default:
      break;
  }
}

/*********************************************************************
 * @fn      pairStateCB
 *
 * @brief   Pairing state callback.
 *
 * @return  none
 */
static void CentPairStateCB( uint16 connHandle, uint8 state, uint8 status )
{
  dbg(">>>> pair ing\n");
  if ( state == GAPBOND_PAIRING_STATE_STARTED )
  {
    dbg( "Pairing started");
  }
  else if ( state == GAPBOND_PAIRING_STATE_COMPLETE )
  {
    if ( status == SUCCESS )
    {
      dbg( "Pairing success" );
    }
    else
    {
      dbg( "Pairing fail:%d\n", status);
    }
  }
  else if ( state == GAPBOND_PAIRING_STATE_BONDED )
  {
    if ( status == SUCCESS )
    {
      dbg( "Bonding success");
    }
  }
}

/*********************************************************************
 * @fn      CentPasscodeCB
 *
 * @brief   Passcode callback.
 *
 * @return  none
 */
static void CentPasscodeCB( uint8 *deviceAddr, uint16 connectionHandle,
                                        uint8 uiInputs, uint8 uiOutputs )
{
//为什么需要判断有没有LCD
// #if (HAL_LCD == TRUE)

  uint32  passcode;
  uint8   str[7];
  
  dbg("?!wtf\n");
  
  // Create random passcode
  LL_Rand( ((uint8 *) &passcode), sizeof( uint32 ));
  passcode %= 1000000;
  // passcode = MAXTAIN_PASSCODE;
  // Display passcode to user
  if ( uiOutputs != 0 )
  {
    dbg( "Passcode:%d",(char *) _ltoa(passcode, str, 10));
  }
  
  // Send passcode response
  GAPBondMgr_PasscodeRsp( connectionHandle, SUCCESS, passcode );
// #endif
}

/*********************************************************************
 * @fn      CentStartDiscovery
 *
 * @brief   Start service discovery.
 *
 * @return  none
 */
static void CentStartDiscovery( void )
{
  dbg("working\n");
  uint8 uuid[ATT_BT_UUID_SIZE] = { LO_UINT16(DATA_SERV_UUID),
                                   HI_UINT16(DATA_SERV_UUID) };
  
  // Initialize cached handles
  centBLESvcStartHdl = centBLESvcEndHdl = centBLECharHdl = 0;

  centBLEDiscState = BLE_DISC_STATE_SVC;
  
  dbg("old one :%x",centConnHandle);
  // Discovery simple BLE service
  GATT_DiscPrimaryServiceByUUID( centConnHandle,
                                 uuid,
                                 ATT_BT_UUID_SIZE,
                                 centBLETaskId );
}
static void CentStartBatt( void )
{
  dbg("working\n");
  uint8 uuid[ATT_BT_UUID_SIZE] = { LO_UINT16(BATT_SERV_UUID),
                                   HI_UINT16(BATT_SERV_UUID) };
  
  // Initialize cached handles
  centBLESvcStartHdl = centBLESvcEndHdl = centBLECharHdl = 0;

  centBLEDiscState = BLE_DISC_STATE_SVC;
  
  dbg("old one :%x",centConnHandle);
  // Discovery simple BLE service
  GATT_DiscPrimaryServiceByUUID( centConnHandle,
                                 uuid,
                                 ATT_BT_UUID_SIZE,
                                 centBLETaskId );
}
/*********************************************************************
 * @fn      centBLEGATTDiscoveryEvent
 *
 * @brief   Process GATT discovery event
 *
 * @return  none
 */
static void centBLEGATTDiscoveryEvent( gattMsgEvent_t *pMsg )
{
  attReadByTypeReq_t req;
  dbg("b1\n");
  if ( centBLEDiscState == BLE_DISC_STATE_SVC )
  {
    dbg("b2\n");
    // Service found, store handles
    if ( pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&
         pMsg->msg.findByTypeValueRsp.numInfo > 0 )
    {
      centBLESvcStartHdl = pMsg->msg.findByTypeValueRsp.handlesInfo[0].handle;
      centBLESvcEndHdl = pMsg->msg.findByTypeValueRsp.handlesInfo[0].grpEndHandle;
    }
     
    // If procedure complete
    if ( ( pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP  && 
           pMsg->hdr.status == bleProcedureComplete ) ||
         ( pMsg->method == ATT_ERROR_RSP ) )
    {
      dbg("b3\n");

      if ( centBLESvcStartHdl != 0 )
      {
        // Discover characteristic
        centBLEDiscState = BLE_DISC_STATE_CHAR;
        
        req.startHandle = centBLESvcStartHdl;
        req.endHandle = centBLESvcEndHdl;
        req.type.len = ATT_BT_UUID_SIZE;
        req.type.uuid[0] = LO_UINT16(DATA_ACK_UUID);
        req.type.uuid[1] = HI_UINT16(DATA_ACK_UUID);
        
        uint8 ret = GATT_ReadUsingCharUUID( centConnHandle, &req, centBLETaskId );
        dbg("what the fuck:%x\n",ret);
      }
    }
  }
  else if ( centBLEDiscState == BLE_DISC_STATE_CHAR )
  {
    // Characteristic found, store handle
    if ( pMsg->method == ATT_READ_BY_TYPE_RSP && 
         pMsg->msg.readByTypeRsp.numPairs > 0 )
    {
      centBLECharHdl = BUILD_UINT16( pMsg->msg.readByTypeRsp.dataList[0],
                                       pMsg->msg.readByTypeRsp.dataList[1] );
      
      dbg( "Data Svc Found\n" );
      centBLEProcedureInProgress = FALSE;

      attWriteReq_t writeReq;

      writeReq.handle = centBLECharHdl; // if you know the value, you can put it in as a fixed value;
                                      // otherwise you will need to first discover the value
      writeReq.len = 2;
      writeReq.value[0] = LO_UINT16(GATT_CLIENT_CFG_NOTIFY);
      writeReq.value[1] = HI_UINT16(GATT_CLIENT_CFG_NOTIFY);
      writeReq.sig = 0;
      writeReq.cmd = 1;
      dbg("notify open");
      GATT_WriteNoRsp( centConnHandle, &writeReq );//centBLETaskId

      osal_start_timerEx(centBLETaskId, TOGGLE_RSSI_POLLING_EVT , TOGGLE_RSSI_POLLING_DELAY); 
    }
    
    centBLEDiscState = BLE_DISC_STATE_IDLE;

  }    
}


/*********************************************************************
 * @fn      centBLEFindSvcUuid
 *
 * @brief   Find a given UUID in an advertiser's service UUID list.
 *
 * @return  TRUE if service UUID found
 */
static bool centBLEFindSvcUuid( uint16 uuid, uint8 *pData, uint8 dataLen )
{
  uint8 adLen;
  uint8 adType;
  uint8 *pEnd;
  
  pEnd = pData + dataLen - 1;
  
  // While end of data not reached
  while ( pData < pEnd )
  {
    // Get length of next AD item
    adLen = *pData++;
    if ( adLen > 0 )
    {
      adType = *pData;
      
      // If AD type is for 16-bit service UUID
      if ( adType == GAP_ADTYPE_16BIT_MORE || adType == GAP_ADTYPE_16BIT_COMPLETE )
      {
        pData++;
        adLen--;
        
        // For each UUID in list
        while ( adLen >= 2 && pData < pEnd )
        {
          // Check for match
          if ( pData[0] == LO_UINT16(uuid) && pData[1] == HI_UINT16(uuid) )
          {
            // Match found
            return TRUE;
          }
          
          // Go to next
          pData += 2;
          adLen -= 2;
        }
        
        // Handle possible erroneous extra byte in UUID list
        if ( adLen == 1 )
        {
          pData++;
        }
      }
      else
      {
        // Go to next item
        pData += adLen;
      }
    }
  }
  
  // Match not found
  return FALSE;
}

/*********************************************************************
 * @fn      centBLEAddDeviceInfo
 *
 * @brief   Add a device to the device discovery result list
 *
 * @return  none
 */
static void centBLEAddDeviceInfo( uint8 *pAddr, uint8 addrType )
{
  uint8 i;
  
  // If result count not at max
  if ( centBLEScanRes < DEFAULT_MAX_SCAN_RES )
  {
    dbg("xx1\n");
    // Check if device is already in scan results
    for ( i = 0; i < centBLEScanRes; i++ )
    {
      if ( osal_memcmp( pAddr, centBLEDevList[i].addr , B_ADDR_LEN ) )
      {
        return;
      }
    }
    
    // Add addr to scan result list
    osal_memcpy( centBLEDevList[centBLEScanRes].addr, pAddr, B_ADDR_LEN );
    centBLEDevList[centBLEScanRes].addrType = addrType;
    
    // Increment scan result count
    centBLEScanRes++;
  }
}


// static void dataChangeCB(uint8 paramID)
// {
//   //直接转发？
//   dbg("Data Changed:%02x\n",paramID);
//   switch(paramID)
//   {
//     case DATA_PACK_CONFIG_NO_OP:
//       dbg("::::06\n");
//       break;
//     case DATA_PACK_CONFIG_SET_NOTIFY:
//       dbg("::::07\n");
//       // notify ?
//       break;
//     case DATA_PACK_RECEIVED:
//       { 
//         uint8 string[13];
//         Data_GetParameter(DATA_PACK,string);
//         if (string[0] == 0xB5 || string[0] == 0x5B)
//         {

//           // infoHandler(centBLETaskId,string);
//         }
//         else  // use ud to process string
//         {
//           // udStrHandler(string);
//         }
        
//       }
//       break;
//     case DATA_ACK_RECEIVED:
//       {
//         dbg("rev ack\n");
//       }
//       break;
//     case DATA_CTRL_RECEIVED:
//       {
//         uint8 datactrl;
//         // Data_GetParameter(DATA_CTRL,&datactrl);
//         // dbg("rcv:0x%02x\n\n",datactrl);
//         if (datactrl == DATACTRL_SYNCREADY)
//         {
//           // dbg("rev 0x22\n");
//           // osal_start_timerEx( centBLETaskId, MWH_SYNC_DATA_EVT, 100 );   //50 is temperary
//         }
//         if (datactrl == DATACTRL_OPENREALTIME)
//         {
//           // __realtimeFlag = OPEN_REALTIME;
//         }

//         if (datactrl == DATACTRL_GET_USERPAGE || datactrl == DATACTRL_GET_SECUPAGE || datactrl == DATACTRL_GET_SETTINGPAGE)
//         {
//           // if (global_statck_busy)
//           // {
//           //   uint8 ack = 0x97; // SETTING_GLOBAL_IS_BUSY
//           //   Data_SetParameter(DATA_ACK,1,&ack);
//           //   return;
//           // }
//           // startSendSetting(HeimDallr_TaskID,datactrl);
//         }

//         if (datactrl == DATACTRL_GOTO_OAD)
//         {
// // #if defined HAL_IMAGE_B
// //       uint16 crc[2] = { 0x0000, 0xFFFF };
// //       uint16 addr = OAD_IMG_R_PAGE * OAD_FLASH_PAGE_MULT + OAD_IMG_CRC_OSET / HAL_FLASH_WORD_SIZE;
// //       HalFlashWrite(addr, (uint8 *)crc, 1);
// //       HAL_SYSTEM_RESET();
// // #endif
      
//         }
//       }
//       break;
//     default: 
//       break;
//   }
// }

/*********************************************************************
 * @fn      bdAddr2Str
 *
 * @brief   Convert Bluetooth address to string
 *
 * @return  none
 */
char *bdAddr2Str( uint8 *pAddr )
{
  uint8       i;
  char        hex[] = "0123456789ABCDEF";
  static char str[B_ADDR_STR_LEN];
  char        *pStr = str;
  
  *pStr++ = '0';
  *pStr++ = 'x';
  
  // Start from end of addr
  pAddr += B_ADDR_LEN;
  
  for ( i = B_ADDR_LEN; i > 0; i-- )
  {
    *pStr++ = hex[*--pAddr >> 4];
    *pStr++ = hex[*pAddr & 0x0F];
  }
  
  *pStr = 0;
  
  return str;
}

/*********************************************************************
*********************************************************************/
